home *** CD-ROM | disk | FTP | other *** search
/ BCI NET / BCI NET Dec 94.iso / archives / programming / source / scrollerwindow.lha / ScrollerWindow.c next >
Encoding:
C/C++ Source or Header  |  1994-06-11  |  23.4 KB  |  958 lines

  1. /*
  2. ****************************************************************************
  3. **
  4. **  $VER: scrollerwindow.c 0.3 (11.6.94)
  5. **
  6. **  Example code which shows how to correctly create a screen resolution
  7. **  sensitive window with scrollbars and arrows.
  8. **
  9. **  Write *ADPAPTIVE* software!  Get *RID* of hard coded values!
  10. **
  11. ****************************************************************************
  12. **
  13. **  Copyright © 1994 Christoph Feck, TowerSystems.  You may use methods
  14. **  and code provided in this example in executables for Commodore-Amiga
  15. **  computers.  All other rights reserved.
  16. **
  17. **  For questions and suggestions contact me via email at:
  18. **  feck@informatik.uni-kl.de
  19. **
  20. **  NOTE:  This file is provided "AS-IS" and subject to change without
  21. **  prior notice; no warranties are made.  All use is at your own risk.
  22. **  No liability or responsibility is assumed.
  23. **
  24. ****************************************************************************
  25. **
  26. **  Compilation notes:
  27. **
  28. **  - Needs V39 or newer includes and amiga.lib (Fred Fish CD or CATS NDK).
  29. **  - This has to be compiled with stack checking off!
  30. **  - HOOK/A0/A1/A2 needs to be changed, if you don't compile with SAS/C.
  31. **
  32. ****************************************************************************
  33. **
  34. **  Sorry if this got too complex, you may also look at older versions :)
  35. **
  36. **  Changes:
  37. **  - gadgetclass doesn't fail on NULL GA_Previous (sorry)
  38. **    (reported by Hartmut Goebel).
  39. **  - closed intuition.library and graphics.library in wrong order
  40. **    (reported by Dave Eaves).
  41. **  - added IDCMP_SIZEVERIFY
  42. **  - GM_LAYOUT for scrollbars (V39)
  43. **  - better buttongclass with delay
  44. **    (suggested by Mark Rose).
  45. **  - no-op backfill hook
  46. **  - keyboard control
  47. **  0.2:
  48. **  - oops!  forgot ReplyMsg()
  49. **  - added input processing
  50. **  - visible based on window size
  51. **  - scrolls the screen in the window :)
  52. **
  53. **  Todo:
  54. **  - drag scrolling
  55. **  - perhaps a picture datatype instead of a cloned screen bitmap?
  56. **  - make it a model
  57. **  - your suggestions/questions
  58. **
  59. ****************************************************************************
  60. */
  61.  
  62. #include <exec/types.h>
  63. #include <exec/memory.h>
  64. #include <exec/libraries.h>
  65. #include <intuition/intuition.h>
  66. #include <intuition/imageclass.h>
  67. #include <intuition/icclass.h>
  68. #include <intuition/gadgetclass.h>
  69.  
  70. #include <proto/exec.h>
  71. #include <proto/intuition.h>
  72. #include <proto/graphics.h>
  73. #include <proto/utility.h>
  74.  
  75.  
  76. /***************************************************************************
  77.  *
  78.  *  Pepo's peculiarities.
  79.  *
  80.  ***************************************************************************
  81.  */
  82.  
  83. #define NEW(type) ((type *) AllocMem(sizeof(type), MEMF_CLEAR | MEMF_PUBLIC))
  84. #define DISPOSE(stuff) (FreeMem(stuff, sizeof(*stuff)))
  85.  
  86. #ifndef IM
  87. #define IM(o) ((struct Image *) o)
  88. #endif
  89.  
  90. #ifndef GAD
  91. #define GAD(o) ((struct Gadget *) o)
  92. #endif
  93.  
  94. #ifndef MAX
  95. #define MAX(x,y) ((x) > (y) ? (x) : (y))
  96. #endif
  97. #ifndef MIN
  98. #define MIN(x,y) ((x) < (y) ? (x) : (y))
  99. #endif
  100.  
  101. /* SAS/C specific */
  102. #define HOOK __saveds __asm
  103. #define A0(stuff) register __a0 stuff
  104. #define A1(stuff) register __a1 stuff
  105. #define A2(stuff) register __a2 stuff
  106.  
  107.  
  108. /***************************************************************************
  109.  *
  110.  *  Global variables.
  111.  *
  112.  ***************************************************************************
  113.  */
  114.  
  115. struct IntuitionBase *IntuitionBase;
  116. struct GfxBase *GfxBase;
  117. struct Library *UtilityBase;
  118. struct Screen *screen;
  119. struct DrawInfo *dri;
  120. BOOL V39;
  121.  
  122. /* We define a subclass of propgclass so
  123.  * we can overload the GM_LAYOUT method.
  124.  */
  125. Class *mypropgclass;
  126.  
  127. /* Our new buttongclass which handles a
  128.  * small delay before the repeat starts,
  129.  * and doesn't send a notification when the
  130.  * button gets released.
  131.  */
  132. Class *mybuttongclass;
  133.  
  134. /* The bitmap we want to display */
  135. struct BitMap *bitmap;
  136.  
  137. /* If TRUE, we can't draw into the window
  138.  * (size verification).
  139.  */
  140. BOOL frozen = FALSE;
  141.  
  142.  
  143. /***************************************************************************
  144.  *
  145.  *  V37 compatible BitMap functions.
  146.  *
  147.  ***************************************************************************
  148.  */
  149.  
  150. struct BitMap *CreateBitMap(LONG width, LONG height, LONG depth, ULONG flags, struct BitMap *friend)
  151. {
  152.     struct BitMap *bm;
  153.  
  154.     if (V39)
  155.     {
  156.         bm = AllocBitMap(width, height, depth, flags, friend);
  157.     }
  158.     else
  159.     {
  160.         LONG memflags = MEMF_CHIP;
  161.  
  162.         if (bm = NEW(struct BitMap))
  163.         {
  164.             InitBitMap(bm, depth, width, height);
  165.             if (flags & BMF_CLEAR) memflags |= MEMF_CLEAR;
  166.             /* For simplicity, we allocate all planes in one big chunk */
  167.             if (bm->Planes[0] = (PLANEPTR) AllocVec(depth * RASSIZE(width, height), memflags))
  168.             {
  169.                 LONG i;
  170.  
  171.                 for (i = 1; i < depth; i++)
  172.                 {
  173.                     bm->Planes[i] = bm->Planes[i - 1] + RASSIZE(width, height);
  174.                 }
  175.             }
  176.             else
  177.             {
  178.                 DISPOSE(bm);
  179.                 bm = NULL;
  180.             }
  181.         }
  182.     }
  183.     return (bm);
  184. }
  185.  
  186.  
  187. VOID DeleteBitMap(struct BitMap *bm)
  188. {
  189.     if (bm)
  190.     {
  191.         if (V39)
  192.         {
  193.             FreeBitMap(bm);
  194.         }
  195.         else
  196.         {
  197.             FreeVec(bm->Planes[0]);
  198.             DISPOSE(bm);
  199.         }
  200.     }
  201. }
  202.  
  203.  
  204. ULONG BitMapDepth(struct BitMap *bm)
  205. {
  206.     if (V39)
  207.     {
  208.         return (GetBitMapAttr(bm, BMA_DEPTH));
  209.     }
  210.     else
  211.     {
  212.         return (bm->Depth);
  213.     }
  214. }
  215.  
  216.  
  217. /***************************************************************************
  218.  *
  219.  *  Calculates the basic size of the resolution.
  220.  *
  221.  ***************************************************************************
  222.  */
  223.  
  224. int SysISize(VOID)
  225. {
  226.     return (screen->Flags & SCREENHIRES ? SYSISIZE_MEDRES : SYSISIZE_LOWRES);
  227. /* NB: SYSISIZE_HIRES not yet supported. */
  228. }
  229.  
  230.  
  231. /***************************************************************************
  232.  *
  233.  *  Object creation stubs.
  234.  *
  235.  ***************************************************************************
  236.  */
  237.  
  238. /* Creates a sysiclass object. */
  239. Object *NewImageObject(ULONG which)
  240. {
  241.     return (NewObject(NULL, SYSICLASS,
  242.      SYSIA_DrawInfo, dri,
  243.      SYSIA_Which, which,
  244.      SYSIA_Size, SysISize(),
  245.     TAG_DONE));
  246. }
  247.  
  248.  
  249. /* Creates an object or our propgclass. */
  250. Object *NewPropObject(ULONG freedom, Tag tag1, ...)
  251. {
  252.     return (NewObject(mypropgclass, NULL,
  253.     /* Send update to IDCMP.  If we make it a model, we would send the
  254.      * notification to our model object. */
  255.      ICA_TARGET, ICTARGET_IDCMP,
  256.      PGA_Freedom, freedom,
  257.      PGA_NewLook, TRUE,
  258.     /* Borderless does only look right with newlook screens */
  259.      PGA_Borderless, ((dri->dri_Flags & DRIF_NEWLOOK) && dri->dri_Depth != 1),
  260.     TAG_MORE, &tag1));
  261. }
  262.  
  263.  
  264. /* Creates an object of our buttongclass. */
  265. Object *NewButtonObject(Object *image, Tag tag1, ...)
  266. {
  267.     return (NewObject(mybuttongclass, NULL,
  268.      ICA_TARGET, ICTARGET_IDCMP,
  269.      GA_Image, image,
  270.     /* No need for GA_Width/Height.  buttongclass is smart :) */
  271.     TAG_MORE, &tag1));
  272. }
  273.  
  274.  
  275. /***************************************************************************
  276.  *
  277.  *  Subclass of buttongclass.  The ROM class has two problems, which make
  278.  *  it not quite usable for scrollarrows.  The first problem is the missing
  279.  *  delay.  Once the next INTUITICK gets send by input.device, the ROM
  280.  *  class already sends a notification.  The other problem is that it also
  281.  *  notifies us, when the button finally gets released (which is necessary
  282.  *  for command buttons).
  283.  *
  284.  *  We define a new class with the GM_GOACTIVE and GM_HANDLEINPUT method
  285.  *  overloaded to work around these problems.
  286.  *
  287.  ***************************************************************************
  288.  */
  289.  
  290. /* Per object instance data */
  291. struct ButtonData
  292. {
  293.     /* The number of ticks we still have to wait
  294.      * before sending any notification.
  295.      */
  296.     ULONG TickCounter;
  297. };
  298.  
  299.  
  300. /* tagcall stub for OM_NOTIFY */
  301. VOID NotifyAttrChanges(Object *o, struct GadgetInfo *gi, ULONG flags, Tag attr1, ...)
  302. {
  303.     DoMethod(o, OM_NOTIFY, &attr1, gi, flags);
  304. }
  305.  
  306.  
  307. ULONG HandleMyButton(struct Gadget *gad, struct gpInput *gpi, struct ButtonData *bd)
  308. {
  309.     UWORD selected = 0;
  310.     struct RastPort *rp;
  311.     ULONG retval = GMR_MEACTIVE;
  312.  
  313.     /* This also works with classic (non-boopsi) images. */
  314.     if (PointInImage((gpi->gpi_Mouse.X << 16) + (gpi->gpi_Mouse.Y), gad->GadgetRender))
  315.     {
  316.         /* We are hit */
  317.         selected = GFLG_SELECTED;
  318.     }
  319.     if (gpi->gpi_IEvent->ie_Class == IECLASS_RAWMOUSE && gpi->gpi_IEvent->ie_Code == SELECTUP)
  320.     {
  321.         /* Gadgetup, time to go */
  322.         retval = GMR_NOREUSE;
  323.         /* Unselect the gadget on our way out... */
  324.         selected = 0;
  325.     }
  326.     if (gpi->gpi_IEvent->ie_Class == IECLASS_TIMER)
  327.     {
  328.         /* We got a tick.  Decrement counter, and if 0, send notify. */
  329.         if (selected && !(--bd->TickCounter))
  330.         {
  331.             bd->TickCounter = 1;
  332.             NotifyAttrChanges((Object *) gad, gpi->gpi_GInfo, 0,
  333.              GA_ID, gad->GadgetID,
  334.             TAG_DONE);
  335.         }
  336.     }
  337.     if ((gad->Flags & GFLG_SELECTED) != selected)
  338.     {
  339.         /* Update changes in gadget render */
  340.         gad->Flags ^= GFLG_SELECTED;
  341.         if (rp = ObtainGIRPort(gpi->gpi_GInfo))
  342.         {
  343.             DoMethod((Object *) gad, GM_RENDER, gpi->gpi_GInfo, rp, GREDRAW_UPDATE);
  344.             ReleaseGIRPort(rp);
  345.         }
  346.     }
  347.     return (retval);
  348. }
  349.  
  350.  
  351. ULONG HOOK DispatchMyButtongClass(A0(Class *cl), A2(Object *o), A1(struct gpInput *gpi))
  352. {
  353.     struct ButtonData *bd = (struct ButtonData *) INST_DATA(cl, o);
  354.  
  355.     switch (gpi->MethodID)
  356.     {
  357.     case GM_GOACTIVE:
  358.         /* May define an attribute to make delay configurable */
  359.         bd->TickCounter = 4;
  360.         /* Notify our target that we have initially hit. */
  361.         NotifyAttrChanges(o, gpi->gpi_GInfo, 0,
  362.          GA_ID, GAD(o)->GadgetID,
  363.         TAG_DONE);
  364.         /* Send more input */
  365.         return (GMR_MEACTIVE);
  366.     case GM_HANDLEINPUT:
  367.         return (HandleMyButton(GAD(o), gpi, bd));
  368.     default:
  369.         /* super class handles everything else */
  370.         return (DoSuperMethodA(cl, o, (Msg) gpi));
  371.     }
  372. }
  373.  
  374.  
  375. /***************************************************************************
  376.  *
  377.  *  Our scroller window.  This is not a boopsi object (yet?).
  378.  *
  379.  ***************************************************************************
  380.  */
  381.  
  382. /* All gadgets and their IDs.
  383.  * Note that we assume they get initialized to NULL.
  384.  */
  385. Object *horizgadget, *vertgadget;
  386. Object *leftgadget, *rightgadget, *upgadget, *downgadget;
  387.  
  388. #define HORIZ_GID    1
  389. #define VERT_GID    2
  390. #define LEFT_GID    3
  391. #define RIGHT_GID    4
  392. #define UP_GID        5
  393. #define DOWN_GID    6
  394.  
  395. struct Window *window;
  396.  
  397. /* These are the images we adapt our layout to. */
  398. Object *sizeimage, *leftimage, *rightimage, *upimage, *downimage;
  399.  
  400. /* Cached model info */
  401. LONG htotal;
  402. LONG vtotal;
  403. LONG hvisible;
  404. LONG vvisible;
  405.  
  406. VOID OpenScrollerWindow(Tag tag1, ...)
  407. {
  408.     int resolution = SysISize();
  409.     WORD topborder = screen->WBorTop + screen->Font->ta_YSize + 1;
  410.     /* Do not use screen->BarHeight, which is the height of the
  411.      * screens bar, not the height of the windows title bar. */
  412.     WORD w = IM(sizeimage)->Width;
  413.     WORD h = IM(sizeimage)->Height;
  414.     WORD bw = (resolution == SYSISIZE_LOWRES) ? 1 : 2;
  415.     WORD bh = (resolution == SYSISIZE_HIRES) ? 2 : 1;
  416.     WORD rw = (resolution == SYSISIZE_HIRES) ? 3 : 2;
  417.     WORD rh = (resolution == SYSISIZE_HIRES) ? 2 : 1;
  418.     WORD gw;
  419.     WORD gh;
  420.     WORD gap;
  421.  
  422.     gh = MAX(IM(leftimage)->Height, h);
  423.     gh = MAX(IM(rightimage)->Height, gh);
  424.     gw = MAX(IM(upimage)->Width, w);
  425.     gw = MAX(IM(downimage)->Width, gw);
  426.  
  427.     /* If you have gadgets in the left window border, set 'gap' to the
  428.      * width of these gadgets. */
  429.     gap = 1;
  430.  
  431.     horizgadget = NewPropObject(FREEHORIZ,
  432.      GA_Left, rw + gap,
  433.      GA_RelBottom, bh - gh + 2,
  434.      GA_RelWidth, -gw - gap - IM(leftimage)->Width - IM(rightimage)->Width - rw - rw,
  435.      GA_Height, gh - bh - bh - 2,
  436.      GA_BottomBorder, TRUE,
  437.      GA_ID, HORIZ_GID,
  438.      PGA_Total, htotal,
  439.      PGA_Visible, hvisible,
  440.     TAG_DONE);
  441.     if (!horizgadget) return;
  442.  
  443.     vertgadget = NewPropObject(FREEVERT,
  444.      GA_RelRight, bw - gw + 3,
  445.      GA_Top, topborder + rh,
  446.      GA_Width, gw - bw - bw - 4,
  447.      GA_RelHeight, -topborder - h - IM(upimage)->Height - IM(downimage)->Height - rh - rh,
  448.      GA_RightBorder, TRUE,
  449.      GA_Previous, horizgadget,
  450.      GA_ID, VERT_GID,
  451.      PGA_Total, vtotal,
  452.      PGA_Visible, vvisible,
  453.     TAG_DONE);
  454.     if (!vertgadget) return;
  455.  
  456.     leftgadget = NewButtonObject(leftimage,
  457.      GA_RelRight, 1 - IM(leftimage)->Width - IM(rightimage)->Width - gw,
  458.      GA_RelBottom, 1 - IM(leftimage)->Height,
  459.      GA_BottomBorder, TRUE,
  460.      GA_Previous, vertgadget,
  461.      GA_ID, LEFT_GID,
  462.     TAG_DONE);
  463.     if (!leftgadget) return;
  464.  
  465.     rightgadget = NewButtonObject(rightimage,
  466.      GA_RelRight, 1 - IM(rightimage)->Width - gw,
  467.      GA_RelBottom, 1 - IM(rightimage)->Height,
  468.      GA_BottomBorder, TRUE,
  469.      GA_Previous, leftgadget,
  470.      GA_ID, RIGHT_GID,
  471.     TAG_DONE);
  472.     if (!rightgadget) return;
  473.  
  474.     upgadget = NewButtonObject(upimage,
  475.      GA_RelRight, 1 - IM(upimage)->Width,
  476.      GA_RelBottom, 1 - IM(upimage)->Height - IM(downimage)->Height - h,
  477.      GA_RightBorder, TRUE,
  478.      GA_Previous, rightgadget,
  479.      GA_ID, UP_GID,
  480.     TAG_DONE);
  481.     if (!upgadget) return;
  482.  
  483.     downgadget = NewButtonObject(downimage,
  484.      GA_RelRight, 1 - IM(downimage)->Width,
  485.      GA_RelBottom, 1 - IM(downimage)->Height - h,
  486.      GA_RightBorder, TRUE,
  487.      GA_Previous, upgadget,
  488.      GA_ID, DOWN_GID,
  489.     TAG_DONE);
  490.     if (!downgadget) return;
  491.  
  492.     window = OpenWindowTags(NULL,
  493.      WA_Gadgets, horizgadget,
  494.      WA_MinWidth, MAX(80, gw + gap + IM(leftimage)->Width + IM(rightimage)->Width + rw + rw + KNOBHMIN),
  495.      WA_MinHeight, MAX(50, topborder + h + IM(upimage)->Height + IM(downimage)->Height + rh + rh + KNOBVMIN),
  496.     TAG_MORE, &tag1);
  497. }
  498.  
  499.  
  500. VOID CloseScrollerWindow(VOID)
  501. {
  502.     if (window) CloseWindow(window);
  503.     DisposeObject(horizgadget);
  504.     DisposeObject(vertgadget);
  505.     DisposeObject(leftgadget);
  506.     DisposeObject(rightgadget);
  507.     DisposeObject(upgadget);
  508.     DisposeObject(downgadget);
  509. }
  510.  
  511.  
  512. /***************************************************************************
  513.  *
  514.  *  Here we do all the stuff necessary to make it work properly.
  515.  *
  516.  ***************************************************************************
  517.  */
  518.  
  519. /* Calculate visible region based on window size.
  520.  *
  521.  * Can't use global 'window' variable, because our layout
  522.  * method calls this before OpenWindow() returns.
  523.  *
  524.  * GZZWidth/GZZHeight are the inner dimensions even for non GZZ windows.
  525.  */
  526.  
  527. #define RecalcHVisible(window) (window->GZZWidth)
  528. #define RecalcVVisible(window) (window->GZZHeight)
  529.  
  530.  
  531. /* This is the dispatcher for our new propgclass.  The
  532.  * idea behind this is to update the scrollbars to adapt
  533.  * to the new window size BEFORE Intuition redraws
  534.  * them.  This is visually attractive.
  535.  *
  536.  * The GM_LAYOUT method is also called when the window
  537.  * opens.
  538.  *
  539.  * Note that GM_LAYOUT is a feature of Intuition V39 and up.
  540.  * This dispatcher will do nothing for V37.
  541.  */
  542.  
  543. ULONG HOOK DispatchMyPropgClass(A0(Class *cl), A2(Object *o), A1(struct gpLayout *gpl))
  544. {
  545.     if (gpl->MethodID == GM_LAYOUT)
  546.     {
  547.         struct Window *win = gpl->gpl_GInfo->gi_Window;
  548.         struct PropInfo *pi = (struct PropInfo *) GAD(o)->SpecialInfo;
  549.         LONG visible;
  550.  
  551.         /* Which one is it? */
  552.         if (pi->Flags & FREEHORIZ)
  553.         {
  554.             hvisible = visible = RecalcHVisible(win);
  555.         }
  556.         else
  557.         {
  558.             vvisible = visible = RecalcVVisible(win);
  559.         }
  560.         /* Do not refresh yourself.  You will be called when it's time. */
  561.         SetAttrs(o, PGA_Visible, visible, TAG_DONE);
  562.         /* fall through */
  563.     }
  564.     /* super class handles everything else */
  565.     return (DoSuperMethodA(cl, o, (Msg) gpl));
  566. }
  567.  
  568.  
  569. /* Copy our BitMap into the window */
  570. VOID CopyBitMap(VOID)
  571. {
  572.     ULONG srcx, srcy;
  573.  
  574.     /* Do not render while in size verification */
  575.     if (!frozen)
  576.     {
  577.         /* Get right place */
  578.         GetAttr(PGA_Top, horizgadget, &srcx);
  579.         GetAttr(PGA_Top, vertgadget, &srcy);
  580.         BltBitMapRastPort(bitmap, srcx, srcy, window->RPort, window->BorderLeft, window->BorderTop, MIN(htotal, hvisible), MIN(vtotal, vvisible), 0xC0);
  581.     }
  582. }
  583.  
  584.  
  585. VOID UpdateProp(Object *gadget, ULONG attr, LONG value)
  586. {
  587.     if (SetAttrs(gadget, attr, value, TAG_DONE))
  588.     {
  589.         struct PropInfo *pi = (struct PropInfo *) (GAD(gadget))->SpecialInfo;
  590.  
  591.         /* Use incremental update.  Avoids flashing.
  592.          * Seems like SetGadgetAttrs() does not recognize
  593.          * (direct) subclasses of propgclass :(
  594.          */
  595.         NewModifyProp(GAD(gadget), window, NULL, pi->Flags, pi->HorizPot, pi->VertPot, pi->HorizBody, pi->VertBody, 1);
  596.     }
  597. }
  598.  
  599.  
  600. VOID UpdateScrollerWindow(VOID)
  601. {
  602.     if (!V39)
  603.     {
  604.         /* Only needed for V37.  With V39, our
  605.          * layout method does the job.
  606.          */
  607.         hvisible = RecalcHVisible(window);
  608.         UpdateProp(horizgadget, PGA_Visible, hvisible);
  609.         vvisible = RecalcVVisible(window);
  610.         UpdateProp(vertgadget, PGA_Visible, vvisible);
  611.     }
  612.     CopyBitMap();
  613. }
  614.  
  615.  
  616. VOID ScrollerLeft(LONG amount)
  617. {
  618.     LONG oldtop;
  619.  
  620.     GetAttr(PGA_Top, horizgadget, (ULONG *) &oldtop);
  621.     if (oldtop > 0)
  622.     {
  623.         UpdateProp(horizgadget, PGA_Top, MAX(0, oldtop - amount));
  624.         CopyBitMap();
  625.     }
  626. }
  627.  
  628.  
  629. VOID ScrollerRight(LONG amount)
  630. {
  631.     LONG oldtop;
  632.  
  633.     GetAttr(PGA_Top, horizgadget, (ULONG *) &oldtop);
  634.     if (oldtop < htotal - hvisible)
  635.     {
  636.         UpdateProp(horizgadget, PGA_Top, MIN(htotal - hvisible, oldtop + amount));
  637.         CopyBitMap();
  638.     }
  639. }
  640.  
  641.  
  642. VOID ScrollerUp(LONG amount)
  643. {
  644.     LONG oldtop;
  645.  
  646.     GetAttr(PGA_Top, vertgadget, (ULONG *) &oldtop);
  647.     if (oldtop > 0)
  648.     {
  649.         UpdateProp(vertgadget, PGA_Top, MAX(0, oldtop - amount));
  650.         CopyBitMap();
  651.     }
  652. }
  653.  
  654.  
  655. VOID ScrollerDown(LONG amount)
  656. {
  657.     LONG oldtop;
  658.  
  659.     GetAttr(PGA_Top, vertgadget, (ULONG *) &oldtop);
  660.     if (oldtop < vtotal - vvisible)
  661.     {
  662.         UpdateProp(vertgadget, PGA_Top, MIN(vtotal - vvisible, oldtop + amount));
  663.         CopyBitMap();
  664.     }
  665. }
  666.  
  667.  
  668. /***************************************************************************
  669.  *
  670.  *  NO-OP backfilling hook.  Since we are going to redraw the whole window
  671.  *  anyway, we can disable backfilling.  This avoids ugly flashing while
  672.  *  resizing or revealing the window.
  673.  *
  674.  *  For V39, you could use WA_BackFill, LAYERS_NOBACKFILL to get the same
  675.  *  effect.
  676.  *
  677.  ***************************************************************************
  678.  */
  679.  
  680. ULONG HOOK BFHookFunc(VOID)
  681. {
  682.     /* Do nothing */
  683.     return (1);
  684. }
  685.  
  686.  
  687. struct Hook BFHook =
  688. {
  689.     NULL, NULL,
  690.     BFHookFunc,
  691. };
  692.  
  693.  
  694. /***************************************************************************
  695.  *
  696.  *  Main program and IDCMP handling.
  697.  *
  698.  ***************************************************************************
  699.  */
  700.  
  701. #define QUAL_SHIFT    (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)
  702. #define QUAL_ALT    (IEQUALIFIER_LALT | IEQUALIFIER_RALT)
  703. #define QUAL_CTRL    (IEQUALIFIER_CONTROL)
  704.  
  705.  
  706. VOID HandleRawKey(UWORD code, UWORD qual)
  707. {
  708.     switch (code)
  709.     {
  710.     case CURSORLEFT:
  711.         if (qual & QUAL_CTRL)
  712.         {
  713.             /* total */
  714.             ScrollerLeft(htotal);
  715.         }
  716.         else if (qual & QUAL_SHIFT)
  717.         {
  718.             /* visible (minus 1 for 'overlap' to match propgclass) */
  719.             ScrollerLeft(hvisible - 1);
  720.         }
  721.         else if (qual & QUAL_ALT)
  722.         {
  723.             /* big step */
  724.             ScrollerLeft(16);
  725.         }
  726.         else
  727.         {
  728.             /* small step */
  729.             ScrollerLeft(1);
  730.         }
  731.         break;
  732.     case CURSORRIGHT:
  733.         if (qual & QUAL_CTRL) ScrollerRight(htotal);
  734.         else if (qual & QUAL_SHIFT) ScrollerRight(hvisible - 1);
  735.         else if (qual & QUAL_ALT) ScrollerRight(16);
  736.         else ScrollerRight(1);
  737.         break;
  738.     case CURSORUP:
  739.         if (qual & QUAL_CTRL) ScrollerUp(vtotal);
  740.         else if (qual & QUAL_SHIFT) ScrollerUp(vvisible - 1);
  741.         else if (qual & QUAL_ALT) ScrollerUp(16);
  742.         else ScrollerUp(1);
  743.         break;
  744.     case CURSORDOWN:
  745.         if (qual & QUAL_CTRL) ScrollerDown(vtotal);
  746.         else if (qual & QUAL_SHIFT) ScrollerDown(vvisible - 1);
  747.         else if (qual & QUAL_ALT) ScrollerDown(16);
  748.         else ScrollerDown(1);
  749.         break;
  750.     default:
  751.         break;
  752.     }
  753. }
  754.  
  755.  
  756. VOID HandleIDCMPUpdate(struct TagItem *attrs)
  757. {
  758.     /* Maybe we want an 'Amount' attribute from our button. */
  759.     LONG amount = 1;
  760.  
  761.     /* We are only interested in the ID of the involved gadget. */
  762.     switch (GetTagData(GA_ID, 0, attrs))
  763.     {
  764.     case HORIZ_GID:
  765.     case VERT_GID:
  766.         CopyBitMap();
  767.         break;
  768.     case LEFT_GID:
  769.         ScrollerLeft(amount);
  770.         break;
  771.     case RIGHT_GID:
  772.         ScrollerRight(amount);
  773.         break;
  774.     case UP_GID:
  775.         ScrollerUp(amount);
  776.         break;
  777.     case DOWN_GID:
  778.         ScrollerDown(amount);
  779.         break;
  780.     default:
  781.         break;
  782.     }
  783. }
  784.  
  785.  
  786. VOID HandleScrollerWindow(VOID)
  787. {
  788.     struct IntuiMessage *imsg;
  789.     BOOL quit = FALSE;
  790.  
  791.     while (!quit)
  792.     {
  793.         while (!quit && (imsg = (struct IntuiMessage *) GetMsg(window->UserPort)))
  794.         {
  795.             switch (imsg->Class)
  796.             {
  797.             case IDCMP_CLOSEWINDOW:
  798.                 quit = TRUE;
  799.                 break;
  800.             case IDCMP_SIZEVERIFY:
  801.                 /* Do not draw until window has been resized. */
  802.                 frozen = TRUE;
  803.                 break;
  804.             case IDCMP_NEWSIZE:
  805.                 frozen = FALSE;
  806.                 UpdateScrollerWindow();
  807.                 break;
  808.             case IDCMP_REFRESHWINDOW:
  809.                 BeginRefresh(window);
  810.                 CopyBitMap();
  811.                 EndRefresh(window, TRUE);
  812.                 break;
  813.             case IDCMP_VANILLAKEY:
  814.                 switch (imsg->Code)
  815.                 {
  816.                 case 'q':
  817.                 case 'Q':
  818.                 case 0x1B: /* ESC */
  819.                     quit = TRUE;
  820.                 default:
  821.                     break;
  822.                 }
  823.                 break;
  824.             case IDCMP_RAWKEY:
  825.                 HandleRawKey(imsg->Code, imsg->Qualifier);
  826.                 break;
  827.             case IDCMP_IDCMPUPDATE:
  828.                 /* IAddress is a pointer to a taglist with new attributes. */
  829.                 HandleIDCMPUpdate((struct TagItem *) imsg->IAddress);
  830.                 break;
  831.             default:
  832.                 break;
  833.             }
  834.             ReplyMsg((struct Message *) imsg);
  835.         }
  836.         if (!quit) WaitPort(window->UserPort);
  837.     }
  838. }
  839.  
  840.  
  841. VOID DoScrollerWindow(VOID)
  842. {
  843.     if (screen = LockPubScreen(NULL))
  844.     {
  845.         /* We clone the screen bitmap */
  846.         hvisible = htotal = screen->Width;
  847.         vvisible = vtotal = screen->Height;
  848.         if (bitmap = CreateBitMap(htotal, vtotal, BitMapDepth(screen->RastPort.BitMap), 0, screen->RastPort.BitMap))
  849.         {
  850.             /* Copy it over */
  851.             BltBitMap(screen->RastPort.BitMap, 0, 0, bitmap, 0, 0, htotal, vtotal, 0xC0, ~0, NULL);
  852.             if (dri = GetScreenDrawInfo(screen))
  853.             {
  854.                 sizeimage = NewImageObject(SIZEIMAGE);
  855.                 leftimage = NewImageObject(LEFTIMAGE);
  856.                 rightimage = NewImageObject(RIGHTIMAGE);
  857.                 upimage = NewImageObject(UPIMAGE);
  858.                 downimage = NewImageObject(DOWNIMAGE);
  859.                 if (sizeimage && leftimage && rightimage && upimage && downimage)
  860.                 {
  861.                     OpenScrollerWindow(WA_PubScreen, screen,
  862.                      WA_Title, "$VER: ScrollerWindow 0.3 (11.6.94)",
  863.                      WA_Flags,
  864.                          WFLG_CLOSEGADGET |
  865.                          WFLG_SIZEGADGET |
  866.                          WFLG_DRAGBAR |
  867.                          WFLG_DEPTHGADGET |
  868.                          WFLG_SIMPLE_REFRESH |
  869.                          WFLG_ACTIVATE |
  870.                          WFLG_NEWLOOKMENUS,
  871.                      WA_IDCMP,
  872.                          IDCMP_CLOSEWINDOW |
  873.                          IDCMP_NEWSIZE |
  874.                          IDCMP_SIZEVERIFY |
  875.                          IDCMP_REFRESHWINDOW |
  876.                          IDCMP_VANILLAKEY |
  877.                          IDCMP_RAWKEY |
  878.                          IDCMP_MOUSEMOVE |
  879.                          IDCMP_MOUSEBUTTONS |
  880.                          IDCMP_INTUITICKS |
  881.                          IDCMP_IDCMPUPDATE,
  882.                      WA_InnerWidth, htotal,
  883.                      WA_InnerHeight, vtotal,
  884.                      /* Limit repeated keyboard events */
  885.                      WA_RptQueue, 2,
  886.                      WA_BackFill, &BFHook,
  887.                      /* We must limit the maximum size to the current size
  888.                       * since we can't EraseRect() outside of the bitmap
  889.                       * (because we have a no-op backfill hook).
  890.                       *
  891.                       * This is not really critical for V37/V39, because
  892.                       * the window can't get bigger than the screen.
  893.                       * But future OS versions might have this feature.
  894.                       */
  895.                      WA_MaxWidth, 0,
  896.                      WA_MaxHeight, 0,
  897.                     TAG_DONE);
  898.                     if (window)
  899.                     {
  900.                         UpdateScrollerWindow();
  901.                         HandleScrollerWindow();
  902.                     }
  903.                     CloseScrollerWindow();
  904.                 }
  905.                 DisposeObject(sizeimage);
  906.                 DisposeObject(leftimage);
  907.                 DisposeObject(rightimage);
  908.                 DisposeObject(upimage);
  909.                 DisposeObject(downimage);
  910.                 FreeScreenDrawInfo(screen, dri);
  911.             }
  912.             WaitBlit();
  913.             DeleteBitMap(bitmap);
  914.         }
  915.         UnlockPubScreen(NULL, screen);
  916.     }
  917. }
  918.  
  919.  
  920. /***************************************************************************
  921.  *
  922.  *  Startup.
  923.  *
  924.  ***************************************************************************
  925.  */
  926.  
  927. void main(int argc, char *argv[])
  928. {
  929.     if (IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 36))
  930.     {
  931.         /* Do we run V39? */
  932.         V39 = ((struct Library *) IntuitionBase)->lib_Version >= 39;
  933.         if (GfxBase = (struct GfxBase *) OpenLibrary("graphics.library", 36))
  934.         {
  935.             if (UtilityBase = OpenLibrary("utility.library", 36))
  936.             {
  937.                 /* Here we create our new classes */
  938.                 if (mypropgclass = MakeClass(NULL, PROPGCLASS, NULL, 0, 0))
  939.                 {
  940.                     mypropgclass->cl_Dispatcher.h_Entry = (ULONG (*)()) DispatchMyPropgClass;
  941.                     if (mybuttongclass = MakeClass(NULL, BUTTONGCLASS, NULL, sizeof(struct ButtonData), 0))
  942.                     {
  943.                         mybuttongclass->cl_Dispatcher.h_Entry = (ULONG (*)()) DispatchMyButtongClass;
  944.                         DoScrollerWindow();
  945.                         FreeClass(mybuttongclass);
  946.                     }
  947.                     FreeClass(mypropgclass);
  948.                 }
  949.                 CloseLibrary(UtilityBase);
  950.             }
  951.             CloseLibrary((struct Library *) GfxBase);
  952.         }
  953.         CloseLibrary((struct Library *) IntuitionBase);
  954.     }
  955. }
  956.  
  957.  
  958.